package org.lonetree.tc.core;

import java.util.Set;
import java.util.HashSet;
import org.lonetree.tc.core.id.NaturaldentifiableItem;
import org.lonetree.tc.core.id.ValidatedIdentifier;

/**
 * Business class representing users in the system.
 * 
 * All interfaces between different system components should use objects of this
 * class to pass information about users.
 */
public final class User extends NaturaldentifiableItem{
	
	private String firstName;
	private String lastName;
	private String password;
	private EmailAddress emailAddress;
	private String location;
	private Set<Section> mySections;
	private Set<Book> myBooks;
	private boolean admin;
	private boolean suspended;
	
	protected User(Username username){
		this(username, null, null, null, null, null, null, null, false, false);
	}
	
	protected User(Username username, String firstName, String lastName, String password, EmailAddress emailAddress, String location, Set<Section> mySections, Set<Book> myBooks, boolean admin, boolean suspended){
		super(username);
		
		this.firstName = firstName;
		this.lastName = lastName;
		this.password = password;
		this.emailAddress = emailAddress;
		this.location = location;
		this.mySections = mySections;
		this.myBooks = myBooks;
		this.admin = admin;
		this.suspended = suspended;
		
		if(mySections == null)
			this.mySections = new HashSet<Section>();
		
		if(myBooks == null)
			this.myBooks = new HashSet<Book>();
	}
		
	/**
	 * Get the Username identifying the user.
	 *
	 * @return Username
	 */
	public Username getUsername(){
		return (Username)getIdentifier();
	}
	
	/**
	 * Get the first name of the user.
	 *
	 * @return first name
	 */
	public String getFirstName(){
		return firstName;
	}
	
	/**
	 * Get the last name of the user.
	 *
	 * @return last name
	 */
	public String getLastName(){
		return lastName;
	}
	
	/**
	 * Get the password of the user.
	 *
	 * @return password
	 */
	public String getPassword(){
		return password;
	}
	
	/**
	 * Set the password of the user.
	 *
	 * @param password - the new password
	 */
	public void setPassword(String password){
		this.password = password;
	}
	
	/**
	 * Get the email address of the user.
	 *
	 * @return email address
	 */
	public EmailAddress getEmailAddress(){
		return emailAddress;
	}
	
	/**
	 * Get the location of the user.
	 *
	 * @return location
	 */
	public String getLocation(){
		return location;
	}
	
	/**
	 * Get the sections the user has added to his or her 'MySections'.
	 *
	 * @return sections Set
	 */
	public Set<Section> getMySections(){
		return mySections;
	}
	
	/**
	 * Get the books the user has added to his or her 'MyBooks'.
	 *
	 * @return books Set
	 */
	public Set<Book> getMyBooks(){
		return myBooks;
	}
	
	/**
	 * Set the first name of the user.
	 *
	 * @param firstName - the new first name
	 */
	public void setFirstName(String firstName){
		this.firstName = firstName;
	}

	/**
	 * Set the last name of the user.
	 *
	 * @param lastName - the new last name
	 */
	public void setLastName(String lastName){
		this.lastName = lastName;
	}
	
	/**
	 * Set the email address of the user.
	 *
	 * @param emailAddress - the new email address
	 */
	public void setEmailAddress(EmailAddress emailAddress){
		this.emailAddress = emailAddress;
	}
	
	/**
	 * Set the location of the user.
	 *
	 * @param location - the new location
	 */
	public void setLocation(String location){
		this.location = location;
	}
	
	/**
	 * Check whether the user is an administrator.
	 *
	 * @return administrator status
	 */
	public boolean isAdmin(){
		return admin;
	}
	
	/**
	 * Set the user's administrator status.
	 *
	 * @param admin - whether the user is an administrator or not
	 */
	public void setAdmin(boolean admin){
		this.admin = admin;
	}
	
	/**
	 * Check whether the user is suspended.
	 *
	 * @return suspended status
	 */
	public boolean isSuspended(){
		return suspended;
	}
	
	/**
	 * Set the user's suspended status.
	 *
	 * @param suspended - whether the user is suspended or not
	 */
	public void setSuspended(boolean suspended){
		this.suspended = suspended;
	}
	
	protected String getIdentifierType(){
		return "username";
	}
	
	
	/**
	 * ValidatedIdentifier representing a user name (or 'user ID'). Usernames
	 * are the unique identifiers for all User objects.
	 */
	public static class Username extends ValidatedIdentifier{
		
		/**
		 * Create a Username encapsulating the passed username.
		 *
		 * @param username - the name representing a user
		 * @throws ValidationException if the username contains characters other
		 * 							   than numbers and letters
		 */
		public Username(String username) throws ValidationException{
			super(username);
		}

		@Override
		protected String validateAndFix(String identifier) throws ValidationException{
			// IMPLEMENT Username.validate()
			return identifier;
		}
		
		/**
		 * Get the String representation of the username.
		 *
		 * @return username
		 */
		public String getUsernameString(){
			return getIdentifierString();
		}
	}
	
	/**
	 * ValidatedIdentifier representing an email address. While EmailAddresses
	 * are not used to uniquely identify any business objects, they do uniquely
	 * identify an address emails can be sent to.
	 */
	public static class EmailAddress extends ValidatedIdentifier{
		
		/**
		 * Create an EmailAddress encapsulating the passed email address.
		 *
		 * @param emailAddress - the String representing an email address
		 * @throws ValidationException if the emailAddress contains more than
		 * 							   or fewer than exactly one '@' symbol,
		 * 							   does not contain a '.' after the '@'
		 * 							   symbol or if it contains characters other
		 * 							   than letters, numbers, '.', '@', '-' or
		 * 							   '_'
		 */
		public EmailAddress(String emailAddress) throws ValidationException{
			super(emailAddress);
		}

		@Override
		protected String validateAndFix(String identifier) throws ValidationException{
			int firstAt = identifier.indexOf('@');
			int lastAt = identifier.lastIndexOf('@');
			int lastDot = identifier.lastIndexOf('.');
			
			try{
				if(firstAt == -1)
					throw new ValidationException("must have exactly one \"@\" - none found");
				
				if(firstAt != lastAt)
					throw new ValidationException("must have exactly one \"@\" - more than one found");
				
				if(lastDot < lastAt + 1)
					throw new ValidationException("must have at least one \".\" after the \"@\" - none found");
				
				validate(identifier.substring(0, lastAt).getBytes());
				validate(identifier.substring(lastAt+1).getBytes());
			}
			catch(Exception e){
				throw new ValidationException("[" + identifier + "] email address " + e.getMessage());
			}
			
			return identifier;
		}
		
		private void validate(byte[] b) throws ValidationException{
			for(int i = 0; i<b.length; i++){
				if(!validate(b[i]))
					throw new ValidationException("cannot have \"" + (char)b[i] + "\"");
			}
		}

		private boolean validate(byte b){
			if(	(b >= 'a' && b <= 'z') ||
				(b >= 'A' && b <= 'Z') ||
				(b >= '0' && b <= '9') ||
				(b == '.') ||
				(b == '_') ||
				(b == '-'))
				return true;
			else
				return false;
				
		}
		
		/**
		 * Get the String representation of the email address.
		 * 
		 * @return email address
		 */
		public String getEmailAddressString(){
			return getIdentifierString();
		}
	}
}
